﻿@using StackExchange.Opserver.Data.Redis
@using StackExchange.Opserver.Views.Redis
@model DashboardModel
@{
    Layout = null;
}
@helper ServerRole (RedisInfo.RedisInstanceRole role) {
    switch (role)
    {
        case RedisInfo.RedisInstanceRole.Master:
            <span class="status-up">Master</span>
            break;
        case RedisInfo.RedisInstanceRole.Slave:
            <span>Slave</span>
            break;
        default:
            <span class="note">Unknown</span>
            break;
    }
}
@helper ServerRow (RedisInstance server, int nest = 0) {
    var info = server.Info.SafeData();
    var clients = server.Clients.SafeData();
    <tr class="@(nest == 0 ? "first-of-group" : "")@(server.SlaveCount == 0 ? (nest == 0 ? " " : "") + "last-of-group" : "")">
        <td class="redis-connection-name">@server.ConnectionInfo.Name</td>
        <td style="padding-left: @(20 * nest)px">@server.IconSpan() <a href="/redis/instance?node=@server.Host:@server.Port" class="node-name-link">@server.Host</a> <span class="note">(@server.Port)</span></td>
        <td>@ServerRole(server.Role)</td>
        <td title="@server.SlaveCount.Pluralize("direct slave"), @server.TotalSlaveCount.ToComma() total
@if (server.SlaveConnections != null) {
foreach (var s in server.SlaveConnections) {
  @:@s.IP:@s.Port (@AppCache.GetHostName(s.IP)) - @s.Status   
}
}">@server.SlaveCount @if(server.TotalSlaveCount > server.SlaveCount){<text> <span class="note">(@(server.TotalSlaveCount - server.SlaveCount))</span></text>}</td>
        @if (info != null)
        {
            var stats = info.Stats;
            <td title="Operations
@stats.TotalCommandsProcessed.Pluralize("command") total
@stats.InstantaneousOpsPerSec.Pluralize("operation") per second">@stats.TotalCommandsProcessed.ToComma() <span class="note">(@stats.InstantaneousOpsPerSec.ToComma())</span></td>
            <td>@info.Memory.UsedMemoryRSS.ToHumanReadableSize()</td>
            <td title="@info.Clients.Connected.Pluralize("client") connected 
@if (clients != null)
{
    var ips = clients.Select(c => c.Address.Split(StringSplits.Colon).First()).GroupBy(ip => ip);
    foreach (var ip in ips.OrderBy(ipg => ipg.Key))
    {
        var name = server.GetServerName(ip.Key);
  @:@ip.Key@(name.HasValue() ? " (" + name + ")" : "") - @ip.Count().Pluralize("connection")
    }                                                                         
}">@info.Clients.Connected</td>
            <td>@info.Server.Version</td>
        } 
        else
        {
            <td colspan="4" class="spark-no-data">No data available</td>
        }
        <td>@server.Info.ToPollSpan()</td>
    </tr>
    if (server.SlaveCount <= 0) { return; }
    foreach (var s in server.SlaveInstances)
    {
        if (s == null) { continue; }
        @ServerRow(s, nest + 1)
    }
}
<style>
    .first-of-group:first-of-type td {
        border-top: solid 1px transparent;
    }
    .first-of-group td {
        border-top: solid 1px #CCC;
    }
    .node-group td {
        padding-left: 4px;
        padding-right: 4px;
    }
</style>
@{
    var missing = Model.Instances.Where(i => i.Info == null || i.Role == RedisInfo.RedisInstanceRole.Unknown);
    var masters = Model.Instances.Where(i => i.IsMaster).ToList();
    var slaving = Model.Instances.Where(i => i.IsSlaving).ToList();
    var mastersWithSlaves = masters.Where(m => m.SlaveCount > 0);
    var standAloneMasters = masters.Where(m => m.SlaveCount == 0);
}
@if (slaving.Any())
{
    <table class="node-dashboard cluster-dashboard">
        <thead class="node-header" data-name="header-slaving">
            <tr class="category-row">
                <th colspan="6">
                    <h3>@MonitorStatus.Warning.IconSpan() @slaving.Count().Pluralize("Slaving Instance")</h3>
                </th>
            </tr>
            <tr>
                <th>Master</th>
                <th>Slave</th>
                <th>Master Status</th>
                <th>Link Status</th>
                <th>Bytes Left</th>
                <th>As of</th>
            </tr>
        </thead>
        <tbody class="node-group" data-name="body-slaving">
        @foreach (var s in slaving)
        {
            var master = s.Master;
            <tr>
                @if (master != null)
                {
                    <td>@master.IconSpan() <a href="?node=@master.Host:@master.Port" class="node-name-link">@master.Host</a> <span class="note">(@master.Port)</span></td>
                }
                else
                {
                    <td class="note">Not Found</td>
                }
                <td>@s.IconSpan() <a href="?node=@s.Host:@s.Port" class="node-name-link">@s.Host</a> <span class="note">(@s.Port)</span></td>
                @if (master != null)
                {
                    var sc = master.SlaveConnections.FirstOrDefault(sci => s.ConnectionInfo.IPAddresses.Any(ip => Equals(ip, sci.IPAddress)));
                    if (sc != null)
                    {
                        <td>@sc.Status</td>
                    }
                    else
                    {
                        <td class="note">unknown</td>
                    }
                }
                else
                {
                    <td class="note">unknown</td>
                }
                <td>@s.Replication.MasterLinkStatus</td>
                @if (s.Replication.MastSyncLeftBytes == -1)
                {
                    <td class="note">n/a</td>
                }
                else if (s.Replication.MastSyncLeftBytes == 0)
                {
                    <td class="note">unknown</td>
                }
                else
                {
                    <td title="@s.Replication.MastSyncLeftBytes.ToComma() bytes left to replicate">@s.Replication.MastSyncLeftBytes.ToHumanReadableSize()</td>
                }
                <td>@s.Info.ToPollSpan()</td>
            </tr>
        }
        </tbody>
    </table>
}
@if (missing.Any())
{
    <table class="node-dashboard cluster-dashboard">
        <thead class="node-header" data-name="header-missing">
            <tr class="category-row">
                <th colspan="4">
                    <h3>@MonitorStatus.Critical.IconSpan() @missing.Count().Pluralize("Missing Instance")</h3>
                </th>
            </tr>
            <tr>
                <th>Host</th>
                <th>Role</th>
                <th>Exception</th>
                <th>As of</th>
            </tr>
        </thead>
        <tbody class="node-group" data-name="body-missing">
        @foreach (var m in missing)
        {
            <tr>
                <td>@m.IconSpan() <a href="#" class="node-name-link">@m.Host</a> <span class="note">(@m.Port)</span></td>
                <td>@ServerRole(m.Role)</td>
                <td title="@m.Info.ErrorMessage" class="note">@m.Info.ErrorMessage.TruncateWithEllipsis(100)</td>
                <td>@m.Info.ToPollSpan()</td>
            </tr>
        }
        </tbody>
    </table>
}
@if (mastersWithSlaves.Any())
{
    <table class="node-dashboard cluster-dashboard">
        <thead class="node-header" data-name="header-group-node-groups">
            <tr class="category-row">
                <th colspan="9">
                    <h3>@mastersWithSlaves.GetWorstStatus().IconSpan() @mastersWithSlaves.Count().Pluralize("Instance Group")
                        <span class="cluster-info">(@((mastersWithSlaves.Count() + mastersWithSlaves.Sum(m => m.TotalSlaveCount)).Pluralize("instance")))</span>
                    </h3>
                </th>
            </tr>
            <tr>
                <th>Name</th>
                <th>Host</th>
                <th>Role</th>
                <th>Slaves</th>
                <th title="Operations">Ops <span class="note">(/sec)</span></th>
                <th>Memory</th>
                <th>Clients</th>
                <th>Version</th>
                <th>As of</th>
            </tr>
        </thead>
        <tbody class="node-group" data-name="group-server-list">
        @foreach (var m in mastersWithSlaves)
        {
            @ServerRow(m)
        }
        </tbody>
    </table>
}
@if (standAloneMasters.Any())
{
    <table class="node-dashboard cluster-dashboard">
        <tbody class="node-header" data-name="header-standalone">
            <tr class="category-row">
                <th colspan="9">
                    <h3>@standAloneMasters.GetWorstStatus().IconSpan() @standAloneMasters.Count().Pluralize("Standalone Master")</h3>
                </th>
            </tr>
            <tr>
                <th>Name</th>
                <th>Host</th>
                <th>Role</th>
                <th>Slaves</th>
                <th title="Operations">Ops <span class="note">(/sec)</span></th>
                <th>Memory</th>
                <th>Clients</th>
                <th>Version</th>
                <th>As of</th>
            </tr>
        </tbody>
        <tbody class="node-group" data-name="body-standalone">
        @foreach (var m in standAloneMasters.OrderBy(m => m.Port).ThenBy(m => m.Name))
        {
            @ServerRow(m)
        }
        </tbody>
    </table>
}